/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *
 * Date:    Feb. 2011
 *
 */

#include "base/intmath.hh"
#include "cpu/edge/pred/piecewise_type.hh"
#include "cpu/base.hh"

template<class Impl>
PiecewiseTypeP<Impl>::PiecewiseTypeP(DerivEdgeCPUParams *params)
    :
    // History bits
    msbHistoryBits(params->pwtMsbHistoryBits),
    lsbHistoryBits(params->pwtLsbHistoryBits),
    // Path table size
    msbPathTableSize(params->pwtMsbPathTableSize),
    lsbPathTableSize(params->pwtLsbPathTableSize),
    // Table size
    msbTableSize(params->pwtMsbTableSize),
    lsbTableSize(params->pwtLsbTableSize),
    // Local history bits
    msbLocalHistoryBits(params->pwtMsbLocalHistoryBits),
    lsbLocalHistoryBits(params->pwtLsbLocalHistoryBits),
    // Local history table size
    msbLocalHistoryTableSize(params->pwtMsbLocalHistoryTableSize),
    lsbLocalHistoryTableSize(params->pwtLsbLocalHistoryTableSize),
    // Others
    weightBits(params->weightBits),
    blockShiftAmt(params->blockShiftAmt)
{
    msbPredictor = new PiecewiseBP<Impl>(msbHistoryBits,
                                         msbLocalHistoryBits,
                                         msbPathTableSize,
                                         msbTableSize,
                                         msbLocalHistoryTableSize,
                                         weightBits,
                                         blockShiftAmt);

    lsbPredictor = new PiecewiseBP<Impl>(lsbHistoryBits,
                                         lsbLocalHistoryBits,
                                         lsbPathTableSize,
                                         lsbTableSize,
                                         lsbLocalHistoryTableSize,
                                         weightBits,
                                         blockShiftAmt);

    if (params->typePiecewiseHistMode == "TypeHistory") {
        // Initialize each predictor to proper mode
        msbPredictor->initMode(PiecewiseBP<Impl>::TypeHistory);
        lsbPredictor->initMode(PiecewiseBP<Impl>::TypeHistory);

        historyMode = TypeHistory;
    } else if (params->typePiecewiseHistMode == "SingleBitHistory") {
        // Initialize each predictor to proper mode
        msbPredictor->initMode(PiecewiseBP<Impl>::SingleBitHistory);
        lsbPredictor->initMode(PiecewiseBP<Impl>::SingleBitHistory);

        historyMode = SingleBitHistory;
    } else {
        fatal("Invalid history mode selected for type piecewise predictor.\n");
    }
} 

template<class Impl>
PiecewiseTypeP<Impl>::~PiecewiseTypeP()
{
    DPRINTF(EdgePredUnit,"PiecewiseTypeP destructed!\n");

    delete msbPredictor;
    delete lsbPredictor;
}

template<class Impl>
void
PiecewiseTypeP<Impl>::reset()
{
    msbPredictor->reset();
    lsbPredictor->reset();
}

template<class Impl>
TheISA::BTypeID
PiecewiseTypeP<Impl>::lookup(BlockID block_id, BlockID oldest_block_id,
        Addr &block_pc, ThreadID tid)
{
    bool msb = msbPredictor->lookup(block_id, oldest_block_id, block_pc, tid);
    bool lsb = lsbPredictor->lookup(block_id, oldest_block_id, block_pc, tid);

    TheISA::BTypeID type_id = typeGen(msb, lsb);

    DPRINTF(EdgePredUnit, "[Bid:%i] @%#x get [Type:%i]\n",
            block_id, block_pc, type_id);

    if (historyMode == TypeHistory) {
        // Speculatively update the history
        msbPredictor->speculativeUpdate(block_pc, block_id, type_id, tid);
        lsbPredictor->speculativeUpdate(block_pc, block_id, type_id, tid);
    }

    assert(type_id != TheISA::InvalidExitType);
    
    return type_id;
}

template<class Impl>
void
PiecewiseTypeP<Impl>::update(BlockID block_id, Addr &block_pc,
        BTypeID actual_type_id, BTypeID pred_type_id, ThreadID tid)
{
    bool msb = (((actual_type_id >> 1) & 0x1) == 1) ? true : false;
    bool lsb = ((actual_type_id & 0x1) == 1) ? true : false;

    bool msb_mispredicted = (((actual_type_id >> 1) & 0x1) !=
            ((pred_type_id >> 1) & 0x1)) ? true : false;

    bool lsb_mispredicted = ((actual_type_id & 0x1) !=
            (pred_type_id & 0x1)) ? true : false;

    msbPredictor->update(block_id, block_pc, tid, msb, msb_mispredicted);
    lsbPredictor->update(block_id, block_pc, tid, lsb, lsb_mispredicted);

    if (historyMode == TypeHistory) {
        // Update the local history non-speculatively
        msbPredictor->updateLocalHistoryTable(block_id, block_pc, actual_type_id,
                                              tid);
        lsbPredictor->updateLocalHistoryTable(block_id, block_pc, actual_type_id,
                                              tid);
    }
}

template<class Impl>
void
PiecewiseTypeP<Impl>::squash(BlockID block_id, ThreadID tid)
{
    msbPredictor->squash(block_id, tid);
    lsbPredictor->squash(block_id, tid);
}

template<class Impl>
void
PiecewiseTypeP<Impl>::squash(Addr block_pc, BlockID block_id,
        BTypeID actual_type_id, BTypeID pred_type_id, ThreadID tid)
{
    bool msb = (((actual_type_id >> 1) & 0x1) == 1) ? true : false;
    bool lsb = ((actual_type_id & 0x1) == 1) ? true : false;

    bool msb_mispredicted = (((actual_type_id >> 1) & 0x1) !=
            ((pred_type_id >> 1) & 0x1)) ? true : false;

    bool lsb_mispredicted = ((actual_type_id & 0x1) !=
            (pred_type_id & 0x1)) ? true : false;

    DPRINTF(EdgePredUnit, "Piecewise type predictor:"
            " actual type id:%i, pred type id:%i"
            " msb_mispredict:%i, lsb_mispredict:%i\n",
            actual_type_id, pred_type_id,
            msb_mispredicted, lsb_mispredicted);

    assert(msb_mispredicted || lsb_mispredicted);

    if (msb_mispredicted) { 
        if (historyMode == TypeHistory) {
            msbPredictor->squash(block_pc, block_id, actual_type_id, tid);
        } else if (historyMode == SingleBitHistory) {
            msbPredictor->squash(block_pc, block_id, msb, tid);
        } else {
            panic("Invalid history mode of type piecewise predictor selected!\n");
        }
    }

    if (lsb_mispredicted) {
        if (historyMode == TypeHistory) {
            lsbPredictor->squash(block_pc, block_id, actual_type_id, tid);
        } else if (historyMode == SingleBitHistory) {
            lsbPredictor->squash(block_pc, block_id, lsb, tid);
        } else {
            panic("Invalid history mode of type piecewise predictor selected!\n");
        }
    }
}

template<class Impl>
TheISA::BTypeID
PiecewiseTypeP<Impl>::typeGen(bool msb, bool lsb)
{
    BTypeID type_id = 0;
    if (lsb) type_id = type_id | 0x1;
    if (msb) type_id = type_id | 0x2;

    return type_id;
}

template <class Impl>
uint64_t
PiecewiseTypeP<Impl>::getGlobalHistory(BlockID block_id, ThreadID tid)
{
    return lsbPredictor->getGlobalHistory(block_id, tid);
}

template <class Impl>
uint64_t
PiecewiseTypeP<Impl>::getPathHistory(BlockID block_id, ThreadID tid)
{
    return lsbPredictor->getPathHistory(block_id, tid);
}

